#include "image.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include<iostream>
using namespace std;
//普通构造函数
Image::Image(int h, int w) :height(h), width(w)
{
    this->height = h;
    this->width = w;
    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    data = (unsigned char**)malloc(sizeof(unsigned char*) * this->height);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < this->height; i++)
    {
        //注意3通道，每个像素3个字节
        data[i] = (unsigned char*)malloc(this->width * 3);
        // 读取像素数据
        for (int j = 0; j < this->width; j++)
        {
            data[i][j] = 0;
        }
    }
}
Image::Image(int h, int w, unsigned char val)//创建的图像像素值都为val;
{
    this->height = h;
    this->width = w;
    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    data = (unsigned char**)malloc(sizeof(unsigned char*) * this->height);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < this->height; i++)
    {
        //注意3通道，每个像素3个字节
        data[i] = (unsigned char*)malloc(this->width * 3);
        // 读取像素数据
        for (int j = 0; j < this->width; j++)
        {
            data[i][j] = val;
        }
    }
}
Image::Image(const char* ImageName)//利用文件名从硬盘加载图像文件成为Image对象;
{
    this->ReadBMP(ImageName);
}
Image::Image(unsigned char* m, int rows, int cols)//从一维静态数组创建Image对象，图像的行数和列数由后面两个参数给出;
{
    this->height = rows;
    this->width = cols;
    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    data = (unsigned char**)malloc(sizeof(unsigned char*) * this->height);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < this->height; i++)
    {
        //注意3通道，每个像素3个字节
        data[i] = (unsigned char*)malloc(this->width * 3);
        // 读取像素数据
        for (int j = 0; j < this->width; j++)
        {
            data[i][j] = *m;
            m++;
        }
    }
}
Image::Image(unsigned char m[][100], int rows)//从静态二维数组创建Image对象，图像的行数（二维数组的第一个维度）由第二个参数rows给出;
{
    this->height = rows;
    this->width = 100;
    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    data = (unsigned char**)malloc(sizeof(unsigned char*) * this->height);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < this->height; i++)
    {
        //注意3通道，每个像素3个字节
        data[i] = (unsigned char*)malloc(this->width * 3);
        // 读取像素数据
        for (int j = 0; j < this->width; j++)
        {
            data[i][j] = m[i][j];
        }
    }
}
Image::Image(unsigned char** m, int h, int w)//从动态数组（二级指针）创建Image对象，图像的行数和列数由后面两个参数给出;
{
    this->height = h;
    this->width = w;
    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    data = (unsigned char**)malloc(sizeof(unsigned char*) * this->height);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < this->height; i++)
    {
        //注意3通道，每个像素3个字节
        data[i] = (unsigned char*)malloc(this->width * 3);
        // 读取像素数据
        for (int j = 0; j < this->width; j++)
        {
            data[i][j] = m[i][j];
        }
    }
}
//拷贝构造函数
Image::Image(Image& im)
{
    if (this->data)
    {
        delete[] this->data;
        this->data = NULL;
    }

    *this = im;
}


//释放堆区数据
void Image::myFree(unsigned char** data, int h)
{
    for (int i = 0; i < h; i++)
    {
        free(data[i]);
        data[i] = NULL;
    }
    free(data);
    data = NULL;
    puts("释放堆区数据");
}
//析构函数
Image::~Image()
{
    this->myFree(this->data, this->height);
}


//从硬盘读入图像文件，存储到image类中
void Image::ReadBMP(const char* filename)
{
    FILE* fp = NULL;  // C标准库的文件指针
    fopen_s(&fp, filename, "rb"); // 二进制读取方式打开文件

    // 读取文件头
    fread(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    // 读取信息头
    fread(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);

    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        //注意3通道，每个像素3个字节
        data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3);
        // 读取像素数据
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            fread(&data[i][j], 1, 1, fp);
        }
    }
    this->height = this->bitmapinfoheader.biHeight;
    this->width = this->bitmapinfoheader.biWidth;
    printf("height = %d width = %d\n", this->height, this->width);

    // 关闭读取的文件
    fclose(fp);
}


//保存bmp图像
void Image::WriteBMP(const char* filename)
{
    FILE* fp = NULL; // 保存文件的文件指针
    fopen_s(&fp, filename, "wb"); // 二进制写入方式打开文件
    // 写入文件头
    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    // 写入信息头
    fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);
    // 写入数据
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        fwrite(data[i], bitmapinfoheader.biWidth * 3, 1, fp);
    }
    // 关闭写入的文件
    fclose(fp);
}
//保存bmp图像
void Image::WriteBMP(const char* filename, unsigned char** data)
{
    FILE* fp = NULL; // 保存文件的文件指针
    fopen_s(&fp, filename, "wb"); // 二进制写入方式打开文件
    // 写入文件头
    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    // 写入信息头
    fwrite(&bitmapinfoheader, sizeof(bitmapinfoheader), 1, fp);
    // 写入数据
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        fwrite(data[i], bitmapinfoheader.biWidth * 3, 1, fp);
    }
    // 关闭写入的文件
    fclose(fp);
}
//保存bmp图像
void Image::WriteBMP(const char* filename, unsigned char** t_data,
    BMPFILEHEADER bmpfileheader, BITMAPINFOHEADER t_bitmapinfoheader)
{
    FILE* fp = NULL; // 保存文件的文件指针
    fopen_s(&fp, filename, "wb"); // 二进制写入方式打开文件
    // 写入文件头
    fwrite(&bmpfileheader, sizeof(bmpfileheader), 1, fp);
    // 写入信息头
    fwrite(&t_bitmapinfoheader, sizeof(t_bitmapinfoheader), 1, fp);
    // 写入数据
    for (int i = 0; i < t_bitmapinfoheader.biHeight; i++)
    {
        fwrite(t_data[i], t_bitmapinfoheader.biWidth * 3, 1, fp);
    }
    // 关闭写入的文件
    fclose(fp);
}


//从文本文件读取
void Image::ReadText(const char* filename)
{
    FILE* fp = NULL;  // C标准库的文件指针
    fopen_s(&fp, filename, "rb"); // 二进制读取方式打开文件

    fread(&bitmapinfoheader.biHeight, sizeof(bitmapinfoheader.biHeight), 1, fp);
    fread(&bitmapinfoheader.biWidth, sizeof(bitmapinfoheader.biWidth), 1, fp);

    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        //注意3通道，每个像素3个字节
        data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3);
        // 读取像素数据
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            fread(&data[i][j], 1, 1, fp);
        }
    }

    // 关闭读取的文件
    fclose(fp);
}
//保存成文本文件
void Image::WriteText(const char* filename)
{
    FILE* fp = NULL; // 保存文件的文件指针
    fopen_s(&fp, filename, "wb"); // 二进制写入方式打开文件
    // 写入信息头
    fwrite(&bitmapinfoheader.biHeight, sizeof(bitmapinfoheader.biHeight), 1, fp);
    fwrite(&bitmapinfoheader.biWidth, sizeof(bitmapinfoheader.biWidth), 1, fp);
    // 写入数据
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        fwrite(data[i], bitmapinfoheader.biWidth * 3, 1, fp);
    }
    // 关闭写入的文件
    fclose(fp);
}


//获取图像中指定点的值
unsigned char& Image::At(int row, int col)
{
    return this->data[row * 3][col * 3];
}
//设置像素(row,col)为某值
void Image::Set(int row, int col, unsigned char value)
{
    //重新读取数据
    unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        t_data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3);
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            t_data[i][j] = data[i][j];
        }
    }
    //修改像数值
    for (int i = 0; i < 3; i++)
    {
        t_data[row * 3 + i][col * 3 + i] = value;
    }
    //保存图像
    this->WriteBMP("setImg.bmp", t_data);
    myFree(t_data, bitmapinfoheader.biHeight);
}
//设置图像所有像素为同一值
void Image::Set(unsigned char value)
{
    //重新读取数据
    unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        t_data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3);
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            t_data[i][j] = data[i][j];
        }
    }
    //修改像数值
    for (int i = 0; i < this->bitmapinfoheader.biHeight; i++)
    {
        for (int j = 0; j < this->bitmapinfoheader.biWidth * 3; j++)
        {
            t_data[i][j] = value;
        }
    }
    //保存图像
    this->WriteBMP("setAllImg.bmp", t_data);
    myFree(t_data, bitmapinfoheader.biHeight);
}


//false 左右，true 上下
void Image::Flip(int code)
{
    //重新读取数据
    unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        t_data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3);
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            t_data[i][j] = data[i][j];
        }
    }

    if (code)
    {
        //上下翻转图片
        for (int i = 0; i < bitmapinfoheader.biHeight / 2; i++)
        {
            unsigned char* temp = t_data[i];
            t_data[i] = t_data[bitmapinfoheader.biHeight - 1 - i];
            t_data[bitmapinfoheader.biHeight - 1 - i] = temp;
        }
        this->WriteBMP("flipImageUpDown.bmp", t_data);
    }
    else
    {
        //左右翻转图片
        for (int i = 0; i < bitmapinfoheader.biHeight; i++)
        {
            for (int j = 0; j < bitmapinfoheader.biWidth * 3 / 2; j += 3)
            {
                unsigned char temp = t_data[i][j];
                t_data[i][j] = t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 2];
                t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 2] = temp;

                unsigned char temp1 = t_data[i][j + 1];
                t_data[i][j + 1] = t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 1];
                t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j - 1] = temp1;

                unsigned char temp2 = t_data[i][j + 2];
                t_data[i][j + 2] = t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j];
                t_data[i][bitmapinfoheader.biWidth * 3 - 1 - j] = temp2;
            }
        }
        this->WriteBMP("flipImageLeftRight.bmp", t_data);
    }

    this->myFree(t_data, this->bitmapinfoheader.biHeight);
}


//图像缩小，放大
//false缩小,true放大
void Image::Resize(int code)
{
    if (code)//放大
    {
        /*	*
            *难点主要在于对索引的控制，
            *因为这里需要两个索引控制四个变量，需要找到它们对应的关系式
            */
        unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight * 2);
        for (int i = 0; i < bitmapinfoheader.biHeight; i++)
        {
            t_data[2 * i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3 * 2);
            for (int j = 0; j < bitmapinfoheader.biWidth; j++)
            {
                t_data[2 * i][6 * j] = data[i][3 * j];
                t_data[2 * i][6 * j + 1] = data[i][3 * j + 1];
                t_data[2 * i][6 * j + 2] = data[i][3 * j + 2];

                t_data[2 * i][6 * j + 3] = data[i][3 * j];
                t_data[2 * i][6 * j + 3 + 1] = data[i][3 * j + 1];
                t_data[2 * i][6 * j + 3 + 2] = data[i][3 * j + 2];
            }

            t_data[2 * i + 1] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3 * 2);
            for (int j = 0; j < bitmapinfoheader.biWidth * 6; j++)
            {
                t_data[2 * i + 1][j] = t_data[2 * i][j];
            }
        }

        BMPFILEHEADER t_bmpfileheader = this->bmpfileheader;
        BITMAPINFOHEADER t_bitmapinfoheader = this->bitmapinfoheader;

        t_bitmapinfoheader.biHeight *= 2;
        t_bitmapinfoheader.biWidth *= 2;
        t_bitmapinfoheader.biSizeImage *= 4;

        this->WriteBMP("magnifyImage.bmp", t_data, t_bmpfileheader, t_bitmapinfoheader);
        this->myFree(t_data, t_bitmapinfoheader.biHeight);//释放内存
    }
    else//缩小
    {
        unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight / 2);
        for (int i = 0; i < bitmapinfoheader.biHeight / 2; i++)
        {
            t_data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3 / 2);
            for (int j = 0, index = 0; j < bitmapinfoheader.biWidth / 2; j++)
            {
                t_data[i][index++] = data[2 * i][6 * j];
                t_data[i][index++] = data[2 * i][6 * j + 1];
                t_data[i][index++] = data[2 * i][6 * j + 2];
            }
        }

        BMPFILEHEADER t_bmpfileheader = this->bmpfileheader;
        BITMAPINFOHEADER t_bitmapinfoheader = this->bitmapinfoheader;

        t_bitmapinfoheader.biHeight /= 2;
        t_bitmapinfoheader.biWidth /= 2;
        t_bitmapinfoheader.biSizeImage /= 4;

        this->WriteBMP("shrinkImage.bmp", t_data, t_bmpfileheader, t_bitmapinfoheader);
        this->myFree(t_data, t_bitmapinfoheader.biHeight);//释放内存
    }
}


//图像裁剪的函数
void Image::Cut(int x1, int y1, int x2, int y2)
{//裁剪点(x1,y1)到点(x2,y2)的图像——(0,120)  (512,360)  h = y2 - y1 = 240, w = x2 - x1 = 512
    if (x2 > x1 && y2 > y1)
    {
        int h = y2 - y1 + 1;
        int w = x2 - x1 + 1;
        while (w % 4)
        {
            w++;
        }
        //重新载入数据
        unsigned char** t_data = (unsigned char**)malloc(h * sizeof(unsigned char*));
        //m,n控制t_data的下标,i,j控制data下标
        for (int i = y1, m = 0; i <= y2; i++, m++)
        {
            t_data[m] = (unsigned char*)malloc(w * 3);
            memset(t_data[m], 0, w * 3);
            for (int j = x1 * 3, n = 0; j <= x2 * 3 + 2; j++, n++)
            {
                t_data[m][n] = data[i][j];
            }
        }
        //重新载入头文件
        BMPFILEHEADER t_bmpfileheader = this->bmpfileheader;
        BITMAPINFOHEADER t_bitmapinfoheader = this->bitmapinfoheader;
        t_bitmapinfoheader.biHeight = h;
        t_bitmapinfoheader.biWidth = w;
        //通过公式重新计算biSize
        t_bitmapinfoheader.biSizeImage = (((w * t_bitmapinfoheader.biBitCount) + 31) / 32 * 4) * h;
        //保存数据
        WriteBMP("cutImage.bmp", t_data, t_bmpfileheader, t_bitmapinfoheader);
        //释放数据
        myFree(t_data, h);
    }
    else
    {
        puts("输入坐标有误");
    }
}


//图像旋转的函数
void Image::Rotate(int degree)//图像旋转的函数（简单起见，旋转角度为90度的整数倍）
{
    BMPFILEHEADER t_bmpfileheader = this->bmpfileheader;
    BITMAPINFOHEADER t_bitmapinfoheader = this->bitmapinfoheader;
    if (degree == 90)
    {
        unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biWidth);
        for (int i = 0; i < bitmapinfoheader.biWidth; i++)
        {
            t_data[i] = (unsigned char*)malloc(bitmapinfoheader.biHeight * 3);
            for (int j = 0; j < bitmapinfoheader.biHeight; j++)
            {
                t_data[i][j * 3] = data[bitmapinfoheader.biHeight - j - 1][3 * i];
                t_data[i][j * 3 + 1] = data[bitmapinfoheader.biHeight - j - 1][3 * i + 1];
                t_data[i][j * 3 + 2] = data[bitmapinfoheader.biHeight - j - 1][3 * i + 2];
            }
        }
        //重写信息头
        t_bitmapinfoheader.biHeight = this->bitmapinfoheader.biWidth;
        t_bitmapinfoheader.biWidth = this->bitmapinfoheader.biHeight;
        //保存图片
        this->WriteBMP("rotate90.bmp", t_data, t_bmpfileheader, t_bitmapinfoheader);
        this->myFree(t_data, t_bitmapinfoheader.biHeight);
    }
    else if (degree == 180)
    {
        unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);
        for (int i = 0; i < bitmapinfoheader.biHeight; i++)
        {
            t_data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3);
            for (int j = 0; j < bitmapinfoheader.biWidth; j++)
            {
                t_data[i][j * 3] = data[bitmapinfoheader.biHeight - i - 1][bitmapinfoheader.biWidth * 3 - j * 3 - 3];
                t_data[i][j * 3 + 1] = data[bitmapinfoheader.biHeight - i - 1][bitmapinfoheader.biWidth * 3 - j * 3 - 3 + 1];
                t_data[i][j * 3 + 2] = data[bitmapinfoheader.biHeight - i - 1][bitmapinfoheader.biWidth * 3 - j * 3 - 3 + 2];
            }
        }
        //保存图片
        this->WriteBMP("rotate180.bmp", t_data);
        this->myFree(t_data, t_bitmapinfoheader.biHeight);
    }
    else if (degree == 270)
    {
        unsigned char** t_data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biWidth);
        for (int i = 0; i < bitmapinfoheader.biWidth; i++)
        {
            t_data[i] = (unsigned char*)malloc(bitmapinfoheader.biHeight * 3);
            for (int j = 0; j < bitmapinfoheader.biHeight; j++)
            {
                t_data[i][j * 3] = data[j][bitmapinfoheader.biWidth * 3 - 3 - i * 3];
                t_data[i][j * 3 + 1] = data[j][bitmapinfoheader.biWidth * 3 - 3 - i * 3 + 1];
                t_data[i][j * 3 + 2] = data[j][bitmapinfoheader.biWidth * 3 - 3 - i * 3 + 2];
            }
        }
        //重写信息头
        t_bitmapinfoheader.biHeight = this->bitmapinfoheader.biWidth;
        t_bitmapinfoheader.biWidth = this->bitmapinfoheader.biHeight;
        //保存图片
        this->WriteBMP("rotate270.bmp", t_data, t_bmpfileheader, t_bitmapinfoheader);
        this->myFree(t_data, t_bitmapinfoheader.biHeight);
    }
    else
    {
        puts("输入的角度不符合要求");
    }
}


//求均值方差
void Image::Mean_Variance(float& m, float& var)//求图像的均值和方差，利用参数输出
{
    float sum = 0;
    float ave = 0;
    float vSum = 0;
    float vAve = 0;
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            sum += this->data[i][j];
        }
    }
    ave = sum / (bitmapinfoheader.biHeight * bitmapinfoheader.biWidth * 3);
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            vSum += (data[i][j] - ave) * (data[i][j] - ave);
        }
    }
    vAve = vSum / (bitmapinfoheader.biHeight * bitmapinfoheader.biWidth * 3);

    m = ave;
    var = vAve;
}


Image& Image::operator=(Image& val)//重载操作符
{
    this->bitmapinfoheader = val.bitmapinfoheader;
    this->bmpfileheader = val.bmpfileheader;
    this->height = val.height;
    this->width = val.width;
    // 动态分配二维数组存储像素数据，注意先申请一个存放指针的数组，
    this->data = (unsigned char**)malloc(sizeof(unsigned char*) * bitmapinfoheader.biHeight);
    //其大小为sizeof(unsigned char*) * bitmapinfoheader.biHeight，这点很容易错
    //申请行指针
    for (int i = 0; i < bitmapinfoheader.biHeight; i++)
    {
        //注意3通道，每个像素3个字节
        this->data[i] = (unsigned char*)malloc(bitmapinfoheader.biWidth * 3);
        // 读取像素数据
        for (int j = 0; j < bitmapinfoheader.biWidth * 3; j++)
        {
            this->data[i][j] = val.data[i][j];
        }
    }

    return *this;
}


//实现友元函数，交换两个Image对象的数据
void Swap(Image& a, Image& b)
{
    Image t(a);
    a = b;
    b = t;
}
